package jsr292.java.lang.invoke;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import jsr292.weaver.Capabilities;
import jsr292.weaver.Debug;
import jsr292.weaver.opt.OptimizerContext;
import jsr292.weaver.opt.OptimizerSnippets;
import jsr292.weaver.opt.OptimizerVarAllocator;

class MHLookupUnreflectConstructor extends MethodHandle {
    private final Constructor<?> constructor;
    
    public MHLookupUnreflectConstructor(Constructor<?> constructor, MethodType methodType) {
        super(methodType);
        this.constructor=constructor;
    }
    
    private Object invoke(Object... arguments) throws Throwable {
        try {
            return constructor.newInstance(arguments);
        } catch (InstantiationException e) {
            throw (LinkageError)new LinkageError().initCause(e);
        } catch (IllegalAccessException e) {
            throw (LinkageError)new LinkageError().initCause(e);
        } catch (InvocationTargetException e) {
            Throwable cause=e.getCause();
            if (cause instanceof RuntimeException) {
                throw (RuntimeException)cause;
            }
            if (cause instanceof Error) {
                throw (Error)cause;
            }
            throw cause;
        }
    }
    
    @Override
    public Object $invoke$() throws Throwable {
        return invoke((Object[])null);
    }
    
    @Override
    public Object $invoke$(Object o1) throws Throwable {
        return invoke(o1);
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2) throws Throwable {
        return invoke(o1, o2);
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3) throws Throwable {
        return invoke(o1, o2, o3);
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4) throws Throwable {
        return invoke(o1, o2, o3, o4);
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4, Object o5) throws Throwable {
        return invoke(o1, o2, o3, o4, o5);
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6) throws Throwable {
        return invoke(o1, o2, o3, o4, o5, o6);
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7) throws Throwable {
        return invoke(o1, o2, o3, o4, o5, o6, o7);
    }
    
    @Override
    public Object $invoke$(Object... rest) throws Throwable {
        return invoke(rest);
    }
    
    @Override
    public boolean isWeavable(Class<?> classBeingRedefined) {
	if (!Access.isVisible(classBeingRedefined, constructor) && !Capabilities.canDefine()) {
	    if (Debug.DEBUG) System.err.println("unreflect constructor: can't accees to "+constructor+" and can't define");
	    return false;
	}
	return true;
    }
    @Override
    public void weave(OptimizerContext context, OptimizerVarAllocator allocator) {
        if (Debug.DEBUG) {
            Debug.checkAllocator(allocator, this);
        }
        
        OptimizerSnippets.unreflectConstructor(context, allocator, constructor);
    }
}
